package com.chat.websocket.util.minio;

import com.chat.websocket.config.MinIOConfig;
import com.chat.websocket.entity.enums.GlobalConstants;
import com.chat.websocket.exception.GlobalException;
import io.minio.*;
import io.minio.errors.ErrorResponseException;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 孙敬佳
 * 2023 \ 12 \ 08
 */

@Slf4j
@Component
public class MinIOUtil {

	@Resource
	private MinioClient minioClient;

	@Resource
	private MinIOConfig minIOConfig;

	/**
	 * 创建桶
	 *
	 * @param bucketName 桶名称
	 */
	public void createBucket(String bucketName) throws Exception {
		if (!StringUtils.hasLength(bucketName)) {
			throw new GlobalException(GlobalConstants.MINIO_BUCKET_NAME_IS_NULL_ERR.getCode(), GlobalConstants.MINIO_BUCKET_NAME_IS_NULL_ERR.getMessage() + bucketName);
		}
		boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
		if (found) {
			throw new GlobalException(GlobalConstants.MINIO_BUCKET_EXISTS_ERR.getCode(), GlobalConstants.MINIO_BUCKET_EXISTS_ERR.getMessage());
		} else {
			minioClient.makeBucket(MakeBucketArgs.builder()
					.bucket(bucketName)
					.build());
		}
	}

	/**
	 * 删除桶
	 *
	 * @param bucketName 桶名称
	 */
	public void deleteBucket(String bucketName) throws Exception {
		if (!StringUtils.hasLength(bucketName)) {
			throw new GlobalException(GlobalConstants.MINIO_BUCKET_NAME_IS_NULL_ERR.getCode(), GlobalConstants.MINIO_BUCKET_NAME_IS_NULL_ERR.getMessage() + bucketName);
		}
		boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
		if (found) {
			minioClient.removeBucket(
					RemoveBucketArgs.builder()
							.bucket(bucketName)
							.build());
		} else {
			throw new GlobalException(GlobalConstants.MINIO_BUCKET_NOT_EXISTS_ERR.getCode(), GlobalConstants.MINIO_BUCKET_NOT_EXISTS_ERR.getMessage() + bucketName);
		}
	}

	/**
	 * 检查桶是否存在
	 *
	 * @param bucketName 桶名称
	 * @return boolean true-存在 false-不存在
	 */
	public boolean checkBucketExist(String bucketName) throws Exception {
		if (!StringUtils.hasLength(bucketName)) {
			throw new GlobalException(GlobalConstants.MINIO_BUCKET_NAME_IS_NULL_ERR.getCode(), GlobalConstants.MINIO_BUCKET_NAME_IS_NULL_ERR.getMessage() + bucketName);
		}
		return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
	}

	/**
	 * 列出所有的桶
	 *
	 * @return 所有桶名的集合
	 */
	public List<Bucket> getAllBucketInfo() throws Exception {
		//列出所有桶
		return minioClient.listBuckets();
	}

	/**
	 * 列出某个桶中的所有文件名
	 * 文件夹名为空时，则直接查询桶下面的数据，否则就查询当前桶下对于文件夹里面的数据
	 *
	 * @param bucketName 桶名称
	 * @param folderName 文件夹名
	 * @param isDeep     是否递归查询
	 */
	public Iterable<Result<Item>> getBucketAllFile(String bucketName, String folderName, Boolean isDeep) throws Exception {
		if (!StringUtils.hasLength(bucketName)) {
			throw new GlobalException(GlobalConstants.MINIO_BUCKET_NAME_IS_NULL_ERR.getCode(), GlobalConstants.MINIO_BUCKET_NAME_IS_NULL_ERR.getMessage() + bucketName);
		}
		if (!StringUtils.hasLength(folderName)) {
			folderName = "";
		}
		return minioClient.listObjects(
				ListObjectsArgs
						.builder()
						.bucket(bucketName)
						.prefix(folderName + "/")
						.recursive(isDeep)
						.build());
	}


	/**
	 * 删除文件夹
	 *
	 * @param bucketName 桶名
	 * @param objectName 文件夹名
	 */
	public Boolean deleteBucketFile(String bucketName, String objectName) {
		if (!StringUtils.hasLength(bucketName)) {
			throw new GlobalException(GlobalConstants.MINIO_BUCKET_NAME_IS_NULL_ERR.getCode(), GlobalConstants.MINIO_BUCKET_NAME_IS_NULL_ERR.getMessage() + bucketName);
		}
		try {
			minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
			return true;
		} catch (Exception e) {
			log.info("删除文件失败", e);
			throw new GlobalException(GlobalConstants.MINIO_DELETE_FILE_ERR.getCode(), GlobalConstants.MINIO_DELETE_FILE_ERR.getMessage());
		}
	}

	/**
	 * 删除文件夹
	 *
	 * @param bucketName 桶名
	 * @param objectName 文件夹名
	 * @param isDeep     是否递归删除
	 * @return
	 */
	public Boolean deleteBucketFolder(String bucketName, String objectName, Boolean isDeep) {
		if (!StringUtils.hasLength(bucketName)) {
			throw new GlobalException(GlobalConstants.MINIO_BUCKET_NAME_IS_NULL_ERR.getCode(), GlobalConstants.MINIO_BUCKET_NAME_IS_NULL_ERR.getMessage() + bucketName);
		}
		try {
			ListObjectsArgs args = ListObjectsArgs.builder().bucket(bucketName).prefix(objectName + "/").recursive(isDeep).build();
			Iterable<Result<Item>> listObjects = minioClient.listObjects(args);
			listObjects.forEach(objectResult -> {
				try {
					Item item = objectResult.get();
					minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(item.objectName()).build());
				} catch (Exception e) {
					log.info("删除文件夹中的文件异常", e);
					throw new GlobalException(GlobalConstants.MINIO_DELETE_FILE_ERR.getCode(), GlobalConstants.MINIO_DELETE_FILE_ERR.getMessage());
				}
			});
			return true;
		} catch (Exception e) {
			log.info("删除文件夹失败", e);
			throw new GlobalException(GlobalConstants.MINIO_DELETE_FILE_ERR.getCode(), GlobalConstants.MINIO_DELETE_FILE_ERR.getMessage());
		}
	}

	/**
	 * 获取文件下载地址
	 *
	 * @param bucketName 桶名
	 * @param objectName 文件名
	 * @param expires    过期时间,默认秒
	 * @param timeUnit   时间单位
	 * @return 文件url链接
	 * @throws Exception GlobalException
	 */
	public String getFileDownloadUrl(String bucketName, String objectName, Integer expires, TimeUnit timeUnit) throws Exception {
		if (!StringUtils.hasLength(bucketName)) {
			throw new GlobalException(GlobalConstants.MINIO_BUCKET_NAME_IS_NULL_ERR.getCode(), GlobalConstants.MINIO_BUCKET_NAME_IS_NULL_ERR.getMessage() + bucketName);
		}
		GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder()
				.method(Method.GET)//下载地址的请求方式
				.bucket(bucketName)
				.object(objectName)
				.expiry(expires, timeUnit)//下载地址过期时间
				.build();
		return minioClient.getPresignedObjectUrl(args);
	}

	/**
	 * 创建文件夹
	 *
	 * @param bucketName 桶名
	 * @param folderName 文件夹名称
	 * @return
	 * @throws Exception
	 */
	public ObjectWriteResponse createBucketFolder(String bucketName, String folderName) throws Exception {
		if (!StringUtils.hasLength(bucketName)) {
			throw new GlobalException(GlobalConstants.MINIO_BUCKET_NAME_IS_NULL_ERR.getCode(), GlobalConstants.MINIO_BUCKET_NAME_IS_NULL_ERR.getMessage() + bucketName);
		}
		if (!checkBucketExist(bucketName)) {
			throw new GlobalException(GlobalConstants.MINIO_BUCKET_NOT_EXISTS_ERR.getCode(), GlobalConstants.MINIO_BUCKET_NOT_EXISTS_ERR.getMessage());
		}
		PutObjectArgs putObjectArgs = PutObjectArgs.builder()
				.bucket(bucketName)
				.object(folderName + "/")
				.stream(new ByteArrayInputStream(new byte[0]), 0, 0)
				.build();
		return minioClient.putObject(putObjectArgs);
	}

	/**
	 * 检测某个桶内是否存在某个文件
	 *
	 * @param objectName 文件名称
	 * @param bucketName 桶名称
	 */
	public boolean getBucketFileExist(String objectName, String bucketName) throws Exception {
		if (!StringUtils.hasLength(bucketName)) {
			throw new GlobalException(GlobalConstants.MINIO_BUCKET_NAME_IS_NULL_ERR.getCode(), GlobalConstants.MINIO_BUCKET_NAME_IS_NULL_ERR.getMessage() + bucketName);
		}
		if (!StringUtils.hasLength(objectName)) {
			throw new GlobalException(GlobalConstants.MINIO_FILE_NAME_IS_NULL_ERR.getCode(), GlobalConstants.MINIO_FILE_NAME_IS_NULL_ERR.getMessage() + bucketName);
		}

		try {
			// 判断文件是否存在
			return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build()) &&
					minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build()) != null;
		} catch (ErrorResponseException e) {
			log.info("文件不存在！", e);
			throw new GlobalException(GlobalConstants.MINIO_FILE_NOT_EXISTS_ERR.getCode(), GlobalConstants.MINIO_FILE_NOT_EXISTS_ERR.getMessage() + bucketName);
		} catch (Exception e) {
			throw new Exception(e);
		}
	}

	/**
	 * 根据MultipartFile file上传文件
	 * minio 采用文件流上传，可以换成下面的文件上传
	 *
	 * @param file       上传的文件
	 * @param bucketName 上传至服务器的桶名称
	 */
	public String uploadFile(MultipartFile file, String bucketName, String folder) throws Exception {

		if (file == null || file.getSize() == 0 || file.isEmpty()) {
			throw new GlobalException(GlobalConstants.MINIO_UP_FILE_IS_NULL_ERR.getCode(), GlobalConstants.MINIO_UP_FILE_IS_NULL_ERR.getMessage());
		}

		if (!this.checkBucketExist(bucketName)) {
			throw new GlobalException(GlobalConstants.MINIO_BUCKET_NOT_EXISTS_ERR.getCode(), GlobalConstants.MINIO_BUCKET_NOT_EXISTS_ERR.getMessage());
		}

		// 获取上传的文件名
		String filename = file.getOriginalFilename();
		assert filename != null;
		//可以选择生成一个minio中存储的文件名称
		String minioFilename = folder + "/" + UUID.randomUUID() + "_" + filename;
		String url = minIOConfig.getUrl();

		InputStream inputStream = file.getInputStream();
		long size = file.getSize();
		String contentType = file.getContentType();

		// Upload known sized input stream.
		minioClient.putObject(
				PutObjectArgs.builder()
						.bucket(bucketName) //上传到指定桶里面
						.object(minioFilename)//文件在minio中存储的名字
						//p1:上传的文件流；p2:上传文件总大小；p3：上传的分片大小
						.stream(inputStream, size, -1) //上传分片文件流大小，如果分文件上传可以采用这种形式
						.contentType(contentType) //文件的类型
						.build());
		return minIOConfig.getUrl() + "/" + bucketName + "/" + minioFilename;
	}
}
